package com.boot.config.component.websocket

import com.alibaba.fastjson.JSON
import com.alibaba.fastjson.JSONObject
import org.apache.commons.lang3.StringUtils
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Component
import java.io.IOException
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.CopyOnWriteArraySet
import javax.websocket.*
import javax.websocket.server.PathParam
import javax.websocket.server.ServerEndpoint


@ServerEndpoint("/imServer/{userId}")
@Component
class WebSocketServer {

    val log: Logger? = LoggerFactory.getLogger(WebSocketServer::class.java)

    // 静态变量，用来记录当前在线连接数。应该把它设计成线程安全的。
    private var onlineCount = 0

    // concurrent包的线程安全Set，用来存放每个客户端对应的MyWebSocket对象。
    private val webSocketMap = ConcurrentHashMap<String, WebSocketServer>()

    // 与某个客户端的连接会话，需要通过它来给客户端发送数据
    private var session: Session? = null

    // 接收userId
    private var userId = ""

    // concurrent包的线程安全Set，用来存放每个客户端对应的Session对象。
    private var sessionSet = CopyOnWriteArraySet<Session>()

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    fun onOpen(session: Session?, @PathParam("userId") userId: String) {
        sessionSet.add(session)
        this.session = session
        this.userId = userId
        if (webSocketMap.containsKey(userId)) {
            webSocketMap.remove(userId)
            webSocketMap[userId] = this
            //加入set中
        } else {
            webSocketMap[userId] = this
            //加入set中
            addOnlineCount()
            //在线数加1
        }
        log!!.info("用户连接: {}, 当前在线人数为: {}", userId, getOnlineCount())
        try {
            sendMessage(session, userId, "连接成功")
        } catch (e: IOException) {
            log!!.error("用户:{} ,网络异常!!!!!!", { userId })
        }
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    fun onClose() {
        sessionSet.remove(session)
        if (webSocketMap.containsKey(userId)) {
            webSocketMap.remove(userId)
            //从set中删除
            subOnlineCount()
        }
        log!!.info("用户退出:" + userId + ",当前在线人数为:" + getOnlineCount())
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    fun onMessage(message: String, session: Session?) {
        log!!.info("用户消息:{}, 报文:{}", { userId }, { message })
        //可以群发消息
        //消息保存到数据库、redis
        if (StringUtils.isNotBlank(message)) {
            try {
                //解析发送的报文
                val jsonObject: JSONObject = JSON.parseObject(message)
                //追加发送人(防止串改)
                jsonObject["fromUserId"] = userId
                val toUserId: String = jsonObject.getString("toUserId")
                //传送给对应toUserId用户的websocket
                if (StringUtils.isNotBlank(toUserId) && webSocketMap.containsKey(toUserId)) {
                    webSocketMap[toUserId]?.sendMessage(jsonObject.toJSONString())
                } else {
                    log!!.error("请求的userId:{} 不在该服务器上", toUserId)
                    //否则不在这个服务器上，发送到mysql或者redis
                }
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
    }

    /**
     * 出现错误
     * @param session
     * @param error
     */
    @OnError
    fun onError(session: Session?, error: Throwable) {
        log!!.error("用户错误:{}, 原因:{}", { userId }, error.message)
        error.printStackTrace()
    }

    /**
     * 实现服务器主动推送
     */
    @Throws(IOException::class)
    fun sendMessage(message: String?) {
        session!!.basicRemote.sendText(message)
    }

    /**
     * 发送自定义消息
     */
    @Throws(IOException::class)
    fun sendInfo(message: String, @PathParam("userId") userId: String) {
        log!!.info("发送消息到:$userId，报文:$message")
        if (StringUtils.isNotBlank(userId) && webSocketMap.containsKey(userId)) {
            webSocketMap[userId]!!.sendMessage(message)
        } else {
            log!!.error("用户{}, 不在线！", { userId })
        }
    }

    /**
     * 发送消息，实践表明，每次浏览器刷新，session会发生变化。
     * @param session
     * @param message
     */
    fun sendMessage(session: Session, message: String?) {
        try {
            session.basicRemote.sendText(java.lang.String.format("%s (From Server，Session ID=%s)", message, session.id))
        } catch (e: IOException) {
            log!!.error("发送消息出错：{}", e.message)
            e.printStackTrace()
        }
    }


    /**
     * 发送消息，实践表明，每次浏览器刷新，session会发生变化。
     * @param session
     * @param userId
     * @param message
     */
    fun sendMessage(session: Session?, userId: String, message: String?) {
        try {
            session?.basicRemote?.sendText(java.lang.String.format("%s (From Server，Session ID=%s)", message, session.id))
        } catch (e: IOException) {
            log!!.error("发送消息出错：{}", e.message)
            e.printStackTrace()
        }
    }

    /**
     * 群发消息
     * @param message
     * @throws IOException
     */
    @Throws(IOException::class)
    fun broadCastInfo(message: String?) {
        for (session in sessionSet) {
            if (session.isOpen) {
                sendMessage(session, message)
            }
        }
    }

    /**
     * 指定Session发送消息
     * @param sessionId
     * @param message
     * @throws IOException
     */
    @Throws(IOException::class)
    fun sendMessage(message: String?, sessionId: String?) {
        var session: Session? = null
        for (s in sessionSet) {
            if (s.id == sessionId) {
                session = s
                break
            }
        }
        if (session != null) {
            sendMessage(session, message)
        } else {
            log!!.warn("没有找到你指定ID的会话：{}", sessionId)
        }
    }

    @Synchronized
    fun getOnlineCount(): Int {
        return onlineCount
    }

    @Synchronized
    fun addOnlineCount() {
        this.onlineCount++
    }

    @Synchronized
    fun subOnlineCount() {
        this.onlineCount--
    }

}